//@author A0096723X
#include "EntriesSorter.h"


EntriesSorter::EntriesSorter(void)
{
}


EntriesSorter::~EntriesSorter(void)
{
}

bool EntriesSorter::isPast(Entry* entry) {
	return entry->isPast();
}
bool EntriesSorter::isToday(Entry* entry) {
	return (entry->isOccuring() || entry->isFloat());
}
bool EntriesSorter::isTomorrow(Entry* entry) {
	return entry->isOccuring(QDate::currentDate().addDays(1));
}
bool EntriesSorter::isTask(Entry* entry) {
	return entry->getEntryType() == CommonUtils::ENTRY_TYPE_TASK;
}

void EntriesSorter::sortByDate(vector<Entry*> &entries) {
	sort(entries.begin(), entries.end(), CommonUtils::greater);//DEPENDENCY ON COMMONUILS
}
void EntriesSorter::defaultSort(vector<Entry*> &entries) {
	vector<Entry*> past;
	vector<Entry*> today;
	vector<Entry*> tomorrow;
	vector<Entry*> future;
	for(int i=0; i<entries.size(); i++) {
		if(isPast(entries[i])) {//if past
			past.push_back(entries[i]);
		}else if(isToday(entries[i])) {//occurs today
			today.push_back(entries[i]);
		}else if(isTomorrow(entries[i])) {//occurs tomorrow
			tomorrow.push_back(entries[i]);
		} else {
			future.push_back(entries[i]);
		}
	}
	sortByDate(past);
	sortByDate(today);
	sortByDate(tomorrow);
	sortByDate(future);
	entries.clear();
	entries.insert(entries.end(), past.begin(), past.end());
	entries.insert(entries.end(), today.begin(), today.end());
	entries.insert(entries.end(), tomorrow.begin(), tomorrow.end());
	entries.insert(entries.end(), future.begin(), future.end());
}

void EntriesSorter::splitVectorStatus(bool target, const vector<Entry*> &entries, vector<Entry*> &found, vector<Entry*> &notFound) {
	found.clear();
	notFound.clear();
	for(int i=0; i< entries.size(); i++) {
		if(entries[i]->getStatus() == target ) {
			found.push_back(entries[i]);
		} else {
			notFound.push_back(entries[i]);
		}
	}//classified them into tasks and not tasks
}
void EntriesSorter::splitVectorDay(QDate target,const vector<Entry*> &entries, vector<Entry*> &found, vector<Entry*> &notFound, CommonEnum::SEARCH_TYPE searchType) {
	found.clear();
	notFound.clear();
	for(int i=0; i< entries.size(); i++) {
		switch(searchType) {//search exact date
		case CommonEnum::SEARCH_TYPE_DEFAULT:
			if(entries[i]->isOccuring( target) ) {
				found.push_back(entries[i]);
			} else {
				notFound.push_back(entries[i]);
			}
			break;
		case CommonEnum::SEARCH_TYPE_CUMULATIVE:
			if(entries[i]->isPast(QDateTime(target)) || entries[i]->isOccuring( target) ) {
				found.push_back(entries[i]);
			} else {
				notFound.push_back(entries[i]);
			}
			break;
		default:
			assert(false);
		}
	}//classified them into tasks and not tasks
}
void EntriesSorter::splitVectorEvent(const vector<Entry*> &entries, vector<Entry*> &found, vector<Entry*> &notFound) {
	found.clear();
	notFound.clear();
	for(int i=0; i< entries.size(); i++) {
		if(entries[i]->getEntryType() == Event::TYPE_ENTRY) {
			found.push_back(entries[i]);
		} else {
			notFound.push_back(entries[i]);
		}
	}//classified them into tasks and not tasks
}
void EntriesSorter::splitVectorTask(const vector<Entry*> &entries, vector<Entry*> &found, vector<Entry*> &notFound) {
	found.clear();
	notFound.clear();
	for(int i=0; i< entries.size(); i++) {
		if(entries[i]->getEntryType() == Task::TYPE_ENTRY) {
			found.push_back(entries[i]);
		} else {
			notFound.push_back(entries[i]);
		}
	}//classified them into tasks and not tasks
}
void EntriesSorter::splitVector(string target, const vector<Entry*> &entries, vector<Entry*> &found, vector<Entry*> &notFound, CommonEnum::SEARCH_TYPE searchType) {
	target = CommonUtils::toLower(target);
	istringstream searchString(target);
	string targetWord;
	switch(searchType) {//search word by word
	case CommonEnum::SEARCH_TYPE_CUMULATIVE:
		notFound.clear();
		found.clear();
		notFound = entries;//assigns the elements of activeEntries into not found
		while(searchString>>targetWord) {
			assignFoundNotFound(targetWord, found, notFound);
		}
		break;
	case CommonEnum::SEARCH_TYPE_DEFAULT:
		notFound.clear();
		found.clear();
		assignFoundNotFoundExact(target, entries, found, notFound);
		break;
	default:
		assert(false);
	}
}

CommonEnum::ReturnCount EntriesSorter::getCount(vector<Entry*> &entries){
	CommonEnum::ReturnCount entriesCount={0,0,0,0,0};
	for(int i=0; i<entries.size(); i++) {
		if(entries[i]->getStatus() == false) {
			countOverdueTodayFuture(entriesCount, entries[i]);
			countEventTask(entriesCount, entries[i]);

		}
	}
	return entriesCount;
}

//private helper functions below
bool EntriesSorter::isFound(string target, Entry* entry) {
	string searchString =CommonUtils::toLower(entry->getSearchString());
	int pos = searchString.find(target);
	return pos != string::npos;
}

void EntriesSorter::countOverdueTodayFuture(CommonEnum::ReturnCount &entriesCount, Entry* entry) {
	if(isPast(entry)){
		entriesCount.numOverdue++;
	}else if(isToday(entry)) {
		entriesCount.numToday++;
	}else{
		entriesCount.numFuture++;
	}
}
void EntriesSorter::countEventTask(CommonEnum::ReturnCount &entriesCount, Entry* entry) {
	if(isTask(entry))
		entriesCount.numTasks++;
	else
		entriesCount.numEvents++;
}

void EntriesSorter::assignFoundNotFound(string target, vector<Entry*> &found, vector<Entry*> &notFound) {
	for(int i=0; i< notFound.size(); i++) {
		if(isFound(target, notFound[i])) {
			found.push_back(notFound[i]);
			notFound.erase(notFound.begin() + i);//erase the found item from notFound
			//reduce index by 1
			i--;
		}
	}
}
void EntriesSorter::assignFoundNotFoundExact(string target, const vector<Entry*> &entries, vector<Entry*> &found, vector<Entry*> &notFound) {
	for(int i=0; i< entries.size(); i++) {
		if(isFound(target, entries[i])) {
			found.push_back(entries[i]);
		} else {
			notFound.push_back(entries[i]);
		}
	}
}